using System;
using System.Collections.Generic;
using System.Text;
using KL.Standard.Domain.MessageQueue;
using KL.Standard.Model.ViewModels.Queue;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace KL.Standard.RabbitMQ
{
    /// <summary>
    /// 构建同意消费者
    /// </summary>
    public  class BaseQueueConcumer<T>:IBaseQueueConcumer<T>
    {
        public RabbitMQFactory<T> Factory { set; get; }

        public ILogger<BaseQueueConcumer<T>> Logger { set; get; }

        private IConnection _connection;
        private IModel _channel;

        public void StartMQClient()
        {
            _connection = Factory.GetConnection();
            _channel = this._connection.CreateModel();
        }
        /// <summary>
        /// 消费消息
        /// </summary>
        /// <param name="concumeAction"></param>
        /// <param name="logMsg"></param>
        /// <param name="queueName"></param>
        /// <typeparam name="K"></typeparam>
        public void ConcumeMsg<K>(Func<MQueueItem<K>, bool> concumeAction,Action<MQueueItem<K>> logMsg, string queueName)
        {
            #region 死信的设置
            //定义死信路由
            _channel.ExchangeDeclare("dead_eag_exchange",ExchangeType.Direct,true);
            
            //参数设置
            IDictionary<string, object> arguments = new Dictionary<string, object>();
            arguments.Add("x-dead-letter-exchange", "dead_eag_exchange");
            arguments.Add("x-dead-letter-routing-key", "dead4msg");
            //创建队列
            _channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false,
                arguments: arguments);
            //定义死信队列
            _channel.QueueDeclare("dead_eag_queue", true, false, false, null);
            //死信路由与死信队列绑定
            _channel.QueueBind("dead_eag_queue","dead_eag_exchange","dead4msg");
            #endregion
            
            #region  主动拉取的方式
            // BasicGetResult result = _channel.BasicGet(queueName, autoAck:false);
            // if (result == null) {
            //     // No message available at this time.
            // }
            // else
            // {
            //     IBasicProperties props = result.BasicProperties;
            //     ReadOnlyMemory<byte> body = result.Body;
            // }
            #endregion
            
            #region 异步的方式
            // var consumer = new AsyncEventingBasicConsumer(channel);
            // consumer.Received += async (ch, ea) =>
            // {
            //     //var body = ea.Body.ToArray();
            //     var message = Encoding.UTF8.GetString(ea.Body.ToArray());
            //     Logger.LogInformation("获取队列{0}消费消息:{1}",queueName,message);
            //     var entity =JsonConvert.DeserializeObject<MQueueItem<T>>(message);
            //     var flag = action(entity);
            //     if(flag)
            //         channel.BasicAck(ea.DeliveryTag, false); //消息ack确认，告诉mq这条队列处理完，可以从mq删除了
            //     channel.BasicAck(ea.DeliveryTag, false);
            //     await Task.Yield();
            //
            // };
            // // this consumer tag identifies the subscription
            // // when it has to be cancelled
            // string consumerTag = channel.BasicConsume(queueName, autoAck: false,"",false,false,null, consumer);
            #endregion
            
            #region 非异步的方式
            var consumer = new EventingBasicConsumer(_channel);
            //prefetchCount:1来告知RabbitMQ,不要同时给一个消费者推送多于 N 个消息，也确保了消费速度和性能
            _channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
             consumer.Received += (model, ea) =>
             {
                 //处理业务
                 var message = Encoding.UTF8.GetString(ea.Body.ToArray());
                 Logger.LogInformation("获取队列{0}消费消息:{1}",queueName,message);
                 var entity =JsonConvert.DeserializeObject<MQueueItem<K>>(message);
                 var flag = concumeAction(entity);
                 if(flag)
                     _channel.BasicAck(ea.DeliveryTag, false); //消息ack确认，告诉mq这条队列处理完，可以从mq删除了
                 else
                 {
                     _channel.BasicReject(ea.DeliveryTag,false);//拒绝任务，并且将自动将队列信息写入到死信队列中
                 }
                
                 
             };
             _channel.BasicConsume(queueName, autoAck: false,"",true,false,null, consumer);
             #endregion   
        }


        public void CloseClient()
        {
            if(_channel!=null)
                _channel.Close();
            if(_connection!=null)
                _connection.Close();
        }
    }
}